using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
//using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D;
using VisualizationPackage;
using Microsoft.Win32;
//using Microsoft.DirectX.Direct3D.CustomVertex;
//using Microsoft.Xna.Framework;

namespace GraphicsAPIs
{
    public partial class DrawingBox : Form
    {
        private bool _drag = false;
        private Vector3 _dragPoint;
        private float xPos = 0, yPos = 75, zPos = -130, angleX = 0, angleY = 0;
        Matrix mTransformation = Matrix.Identity;
        Matrix mPosition = Matrix.Identity;
        private Matrix _transformation = Matrix.LookAtLH(
                 new Vector3(50f, 75f, -75f), new Vector3(55f, 10f, 0f), new Vector3(0f, 1f, 0f));
        private Device _dev;
        public Device device
        {
            get { return _dev; }
            set { _dev = value; }
        }
        //VertexBuffer vb;
        protected List<VertexBuffer> Polygons;
        protected VertexBuffer LinesVb;

        public DrawingBox()
        {
            InitializeComponent();
            InitializeDevice();
            InitializeCam();
            device.DeviceReset += new EventHandler(device_DeviceReset);
        }

        void device_DeviceReset(object sender, EventArgs e)
        {
            Device dev = (Device)sender;
            // Turn off culling, so we see the front and back of the triangle
            dev.RenderState.CullMode = Cull.None;
            // Turn off D3D lighting, since we are providing our own vertex colors
            dev.RenderState.Lighting = false;
            InitializeDevice();
        }

        public void InitializeDevice()
        {
            PresentParameters pres = new PresentParameters();
            pres.Windowed = true;
            pres.SwapEffect = SwapEffect.Discard;
            pres.BackBufferCount = 1;
            pres.BackBufferFormat = Find16BitMode();
            pres.EnableAutoDepthStencil = true;
            pres.AutoDepthStencilFormat = DepthFormat.D16;
            device = new Device(0, DeviceType.Hardware, this.Handle, CreateFlags.SoftwareVertexProcessing, pres);
            device.RenderState.CullMode = Cull.None;
            device.RenderState.AntiAliasedLineEnable = true;

            InitializeCam();
            device.RenderState.Lighting = false;
            mTransformation.Translate(50, 50, -50);
            //InitializeLight();
        }
        public Format Find16BitMode()
        {
            Format[] poss_formats = { Format.R5G6B5, Format.X1R5G5B5 };
            DeviceType dev = DeviceType.Hardware;

            foreach (Format format in poss_formats)
            {

                if (Manager.CheckDeviceType(0, dev, format, format, false))
                {
                    return format;
                }
            }

            return Format.Unknown;

        }


        private void InitializeLight()
        {
            device.Lights[0].Type = LightType.Point;
            device.Lights[0].Position = new Vector3(-50f, -75f, 75f);
            device.Lights[0].Diffuse = System.Drawing.Color.White;
            device.Lights[0].Attenuation0 = 0.3f;
            device.Lights[0].Range = 10000.0f;
            device.Lights[0].Update();
            device.Lights[0].Enabled = false;
        }

        private void InitializeCam()
        {
            device.Transform.Projection = Matrix.PerspectiveFovLH(
                (float)Math.PI / 1.8f, Width / Height, 1f, 300f);
            device.Transform.View = Matrix.LookAtLH(
                 new Vector3(xPos, yPos, zPos), new Vector3(0, 0f, 0f), new Vector3(0f, 1f, 0f));
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            //pe.Graphics.Clear(Color.White);
            //try
            {
                if (device == null) return;
                InitializeCam();
                device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Gray, 1.0f, 1);
                device.BeginScene();

                //device.Transform.View = _transformation;
                device.VertexFormat = CustomVertex.PositionNormalColored.Format;

                if (Polygons != null && Polygons.Count > 0)
                {
                    foreach (VertexBuffer buffer in Polygons)
                    {
                        device.SetStreamSource(0, buffer, 0);
                        device.VertexFormat = CustomVertex.PositionNormalColored.Format;
                        device.DrawPrimitives(PrimitiveType.TriangleList, 0, (int)(buffer.SizeInBytes / CustomVertex.PositionNormalColored.StrideSize / 3));

                        // device.DrawUserPrimitives(PrimitiveType.LineList, (int)(LinesVb.SizeInBytes / Cu stomVertex.PositionNormalColored.StrideSize / 2), LinesVb);
                        // device.DrawUserPrimitives(PrimitiveType.TriangleFan, (int)(buffer.SizeInBytes / CustomVertex.PositionNormalColored.StrideSize - 2), buffer);
                        // device.DrawUserPrimitives(PrimitiveType.TriangleFan, (int)(Lines.Length / CustomVertex.PositionNormalColored.StrideSize - 2), buffer);
                    }
                }
                if (LinesVb != null)
                {
                    device.SetStreamSource(0, LinesVb, 0);
                    device.VertexFormat = CustomVertex.PositionNormalColored.Format;
                    device.DrawPrimitives(PrimitiveType.LineList, 0, (int)(LinesVb.SizeInBytes / CustomVertex.PositionNormalColored.StrideSize / 2));

                    //                  device.DrawUserPrimitives(PrimitiveType.LineList, (int)(LinesVb.SizeInBytes / CustomVertex.PositionNormalColored.StrideSize / 2), LinesVb);
                }
                device.EndScene();
                device.Present();
            }
            //catch (Exception ex)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        public void End()
        {
            device.Dispose();
        }

        Vector3 CalculateNormal(CustomVertex.PositionNormalColored[] points)
        {
            Vector3 v1 = new Vector3(points[2].X - points[0].X, points[2].Y - points[0].Y, points[2].Z - points[0].Z);
            Vector3 v2 = new Vector3(points[3].X - points[1].X, points[3].Y - points[1].Y, points[3].Z - points[1].Z);
            return Vector3.Normalize(Vector3.Cross(v1, v2));
        }
        public void SetPolygons(List<VisualizationPackage.FileLoader.Polygon> pols)
        {
            //Lines = null;
            Polygons = new List<VertexBuffer>();
            //CustomVertex.PositionNormalColored[] points = new CustomVertex.PositionNormalColored[nVerts*pols];
            List<CustomVertex.PositionNormalColored> points = new List<CustomVertex.PositionNormalColored>();
            int totalVerts = 0;
            foreach (VisualizationPackage.FileLoader.Polygon p in pols)
                totalVerts += (p.Vertics.Count - 2) * 3;
            
            if (totalVerts == 0)
                return;

            VertexBuffer vb = new VertexBuffer(typeof(CustomVertex.PositionNormalColored), totalVerts, device, Usage.Dynamic | Usage.WriteOnly, VertexFormats.Transformed, Pool.Default);
            GraphicsStream current = vb.Lock(0, totalVerts * CustomVertex.PositionNormalColored.StrideSize, LockFlags.Discard);

            int totalPols = 0;
            foreach (VisualizationPackage.FileLoader.Polygon p in pols)
            {
                int nVerts = p.Vertics.Count;
                totalPols += nVerts;
                //    CustomVertex.PositionNormalColored[] points = new CustomVertex.PositionNormalColored[nVerts];
                //int i = 0;
                //foreach (VisualizationPackage.FileLoader.Vertex vert in p.Vertics)
                for (int i = 2; i <= nVerts; i += 2)
                {
                    VisualizationPackage.FileLoader.Vertex vert = p.Vertics[i - 2];
                    VisualizationPackage.FileLoader.Vertex vert2 = p.Vertics[i - 1];
                    VisualizationPackage.FileLoader.Vertex vert3 = p.Vertics[i % nVerts];
                    CustomVertex.PositionNormalColored V1 = new CustomVertex.PositionNormalColored((float)vert.X, (float)vert.Y, (float)vert.Z, 0f, 0f, 1f, p.DrawingColor.ToArgb());
                    CustomVertex.PositionNormalColored V2 = new CustomVertex.PositionNormalColored((float)vert2.X, (float)vert2.Y, (float)vert2.Z, 0f, 0f, 1f, p.DrawingColor.ToArgb());
                    CustomVertex.PositionNormalColored V3 = new CustomVertex.PositionNormalColored((float)vert3.X, (float)vert3.Y, (float)vert3.Z, 0f, 0f, 1f, p.DrawingColor.ToArgb());
                    current.Write(V1);
                    current.Write(V2);
                    current.Write(V3);
                }
                //Vector3 vec = CalculateNormal(points);
                //while (--i >= 0)
                //    points[i].Normal = vec;
                //VertexBuffer vb = new VertexBuffer(typeof(CustomVertex.PositionNormalColored), nVerts, device, Usage.Dynamic | Usage.WriteOnly, VertexFormats.Transformed, Pool.Default);
                //VertexBuffer vb = new VertexBuffer(device, nVerts * CustomVertex.PositionNormalColored.StrideSize, Usage.None, VertexFormats.Transformed, Pool.Managed);
                //GraphicsStream current = vb.Lock(0, nVerts * CustomVertex.PositionNormalColored.StrideSize, LockFlags.None);
                //current.Write(points);
                //vb.Unlock();
                //Polygons.Add(vb);

            }
            //Array arr = points.ToArray();
            //current.Write(arr);
            vb.Unlock();
            Polygons.Add(vb);
        }

        public void SetLines(List<VisualizationPackage.FileLoader.Line> lines)
        {
            if (lines.Count == 0)
                return;
            //if(Polygons != null)
            //Polygons.Clear();
            CustomVertex.PositionNormalColored V1, V2;
            LinesVb = new VertexBuffer(typeof(CustomVertex.PositionNormalColored), lines.Count * 2, device, Usage.Dynamic | Usage.WriteOnly, VertexFormats.Transformed, Pool.Default);
            //LinesVb = new VertexBuffer(device, 2 * lines.Count * CustomVertex.PositionColored.StrideSize, Usage.Dynamic | Usage.WriteOnly, VertexFormats.Transformed, Pool.Managed);
            GraphicsStream Lines = LinesVb.Lock(0, 2 * lines.Count * CustomVertex.PositionNormalColored.StrideSize, LockFlags.Discard);

            foreach (VisualizationPackage.FileLoader.Line ln in lines)
            {
                V1 = new CustomVertex.PositionNormalColored((float)ln.Vertex1.X, (float)ln.Vertex1.Y, (float)ln.Vertex1.Z, 0.0f, 0.0f, 1.0f, ln.DrawingColor.ToArgb());
                V2 = new CustomVertex.PositionNormalColored((float)ln.Vertex2.X, (float)ln.Vertex2.Y, (float)ln.Vertex2.Z, 0.0f, 0.0f, 1.0f, ln.DrawingColor.ToArgb());
                Lines.Write(V1);
                Lines.Write(V2);//new CustomVertex.PositionColored[] { V1, V2 });
            }
            LinesVb.Unlock();
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            _drag = true;
            _dragPoint = GetTrackBallPoint(new Vector2(e.X, e.Y));
            base.OnMouseDown(e);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_drag)
            {
                Vector3 PointB = GetTrackBallPoint(new Vector2(e.X, e.Y));
                PointB.Normalize();
                _dragPoint.Normalize();
                Vector3 Normal = Vector3.Cross(_dragPoint, PointB);
                Normal.Normalize();
                float Angle = (float)Math.Acos((double)Vector3.Dot(_dragPoint, PointB));

                Matrix mat = Matrix.Translation(-1 * _dragPoint) * Matrix.RotationAxis(Normal, Angle) * Matrix.Translation(_dragPoint);

                mat.M11 = (float)(1 + (1 - Math.Cos(Angle)) * (Normal.X * Normal.X - 1));
                mat.M12 = (float)(-Normal.Z * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.X * Normal.Y);
                mat.M13 = (float)(Normal.Y * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.X * Normal.Y);
                mat.M14 = 0;

                mat.M21 = (float)(Normal.Z * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.X * Normal.Y);
                mat.M22 = (float)(1 + (1 - Math.Cos(Angle)) * (Normal.Y * Normal.Y - 1));
                mat.M23 = (float)(-Normal.X * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.Z * Normal.Y);
                mat.M24 = 0;

                mat.M31 = (float)(-Normal.Y * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.X * Normal.Z);
                mat.M32 = (float)(Normal.X * Math.Sin(Angle) + (1 - Math.Cos(Angle)) * Normal.Z * Normal.Y);
                mat.M33 = (float)(1 + (1 - Math.Cos(Angle)) * (Normal.Z * Normal.Z - 1));
                mat.M34 = 0;

                mat.M41 = 0;
                mat.M42 = 0;
                mat.M43 = 0;
                mat.M44 = 1;

                _transformation = Matrix.Translation(-1 * _dragPoint) * Matrix.RotationAxis(Normal, Angle) * Matrix.Translation(_dragPoint);
                _dragPoint = PointB;
                Invalidate();
            }
            base.OnMouseMove(e);
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            _drag = false;
            base.OnMouseUp(e);
        }
        public static Vector3 GetTrackBallPoint(Vector2 vector)
        {
            float len = vector.X * vector.X + vector.Y + vector.Y;
            if (len > 1)
                return new Vector3(vector.X / len, vector.Y / len, 0);
            return new Vector3(vector.X, vector.Y, (float)Math.Sqrt(1 - len));
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            Quaternion q1 = new Quaternion(xPos, yPos, zPos, 0);
            //Q2 = (cos(theta/2), rx sin(theta/2), ry sin(theta/2), rz sin(theta/2)). 
            Quaternion q2 = new Quaternion();
            Quaternion q3 = new Quaternion();
            float angle = 0.02f;
            bool updateQ = false;
            switch (e.KeyData)
            {
                case Keys.Up:
                    q2 = new Quaternion((float)Math.Sin(angle), 0, 0, (float)Math.Cos(angle));
                    q3 = new Quaternion((float)Math.Sin(angle), 0, 0, (float)Math.Cos(angle));
                    updateQ = true;
                    break;
                case Keys.Right:
                    q2 = new Quaternion(0, (float)Math.Sin(angle), 0, (float)Math.Cos(angle));
                    q3 = new Quaternion(0, (float)Math.Sin(angle), 0, (float)Math.Cos(angle));
                    updateQ = true;
                    break;
                case Keys.Left:
                    q2 = new Quaternion(0, (float)Math.Sin(-angle), 0, (float)Math.Cos(-angle));
                    q3 = new Quaternion(0, (float)Math.Sin(-angle), 0, (float)Math.Cos(-angle));
                    updateQ = true;
                    break;
                case Keys.Down:
                    q2 = new Quaternion((float)Math.Sin(-angle), 0, 0, (float)Math.Cos(-angle));
                    q3 = new Quaternion((float)Math.Sin(-angle), 0, 0, (float)Math.Cos(-angle));
                    updateQ = true;
                    break;
                case Keys.Z:
                    yPos = yPos / xPos * (xPos + 1);
                    zPos = zPos / xPos * (xPos + 1);
                    xPos++;
                    break;
                case Keys.A:
                    yPos = yPos / xPos * (xPos - 1);
                    zPos = zPos / xPos * (xPos - 1);
                    xPos--;
                    break;
                default:
                    return;
            }
            if (updateQ)
            {
                q2.Normalize();
                q3.Normalize();

                q3.Invert();
                Quaternion q = q2 * q1 * q3;
                xPos = q.X;
                yPos = q.Y;
                zPos = q.Z;
            }

            Refresh();
            base.OnKeyDown(e);
        }
        /*

        private void Track(Point currentPosition)
        {
            Vector3 currentPosition3D = ProjectToTrackball(this.Width, this.Height, currentPosition);

            Vector3 axis = Vector3.Cross(_previousPosition3D, currentPosition3D);
            double angle = Vector3.AngleBetween(_previousPosition3D, currentPosition3D);
            float Angle = (float)Math.Acos((double)Vector3.Dot(_previousPosition3D, currentPosition3D));
            Quaternion delta = new Quaternion(axis, -angle);

            // Get the current orientantion from the RotateTransform3D
            AxisAngleRotation3D r = _rotation;
            Quaternion q = new Quaternion(_rotation.Axis, _rotation.Angle);

            // Compose the delta with the previous orientation
            q *= delta;

            // Write the new orientation back to the Rotation3D
            _rotation.Axis = q.Axis;
            _rotation.Angle = q.Angle;

            _previousPosition3D = currentPosition3D;
        }

        private Vector3 ProjectToTrackball(double width, double height, Point point)
        {
            double x = point.X / (width / 2);    // Scale so bounds map to [0,0] - [2,2]
            double y = point.Y / (height / 2);

            x = x - 1;                           // Translate 0,0 to the center
            y = 1 - y;                           // Flip so +Y is up instead of down

            double z2 = 1 - x * x - y * y;       // z^2 = 1 - x^2 - y^2
            double z = z2 > 0 ? Math.Sqrt(z2) : 0;

            return new Vector3(x, y, z);
        }*/

    }
}
/*
 * using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Generic;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.Direct3D.CustomVertex;

namespace GraphicsAPIs
{
	public partial class DrawingBox : PictureBox
	{
		private Device _dev;
		public Device device
		{
			get { return _dev; }
			set { _dev = value; }
		}
		List < GraphicsBuffer<PositionColored> > Polygons;
		GraphicsBuffer<PositionColored> Lines;

		public DrawingBox()
		{
			InitializeComponent();
			InitializeDevice();
			InitializeCam();
		}
		
		public void InitializeDevice()
		{
			PresentParameters pres = new PresentParameters();
			pres.IsWindowed = true;
			pres.SwapEffect = SwapEffect.Discard;
			pres.EnableAutoDepthStencil = true;
			pres.AutoDepthStencilFormat = DepthFormat.D16;
			device = new Device(0, DeviceType.Hardware, this.Handle, CreateFlags.SoftwareVertexProcessing, pres);
			device.RenderState.CullMode = Cull.None;
			device.RenderState.Lighting = false;
			InitializeCam();
		}



		private void InitializeCam()
		{
			device.Transform.Projection = Matrix.PerspectiveFieldOfViewLeftHanded(
				(float)Math.PI / 2f, Width / Height, 1f, 250f);
			device.Transform.View = Matrix.LookAtLeftHanded(
				 new Vector3(0f, 0f, -100f), new Vector3(35f, 10f, 0f), new Vector3(0f, 1f, 0f));
		}


		protected override void OnPaint(PaintEventArgs pe)
		{
			try
			{
				if (device == null) return;
				device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.CornflowerBlue, 1.0f, 0);
				device.BeginScene();
				device.VertexFormat = PositionColored.Format;
				if (Polygons != null && Polygons.Count > 0)
				{
					foreach (GraphicsBuffer<PositionColored> buffer in Polygons)
						device.DrawUserPrimitives(PrimitiveType.TriangleStrip, buffer.ElementCount - 2, buffer);
				}
				if (Lines != null)
					device.DrawUserPrimitives(PrimitiveType.LineList, Lines.ElementCount / 2, Lines);
				device.EndScene();
				device.Present();
			}
			catch { }
		}

		public void End()
		{
			device.Dispose();
		}

		public void SetPolygons(List<VisualizationPackage.FileLoader.Polygon> pols)
		{
			Lines = null;
			Polygons = new List<GraphicsBuffer<PositionColored>>();
			foreach (VisualizationPackage.FileLoader.Polygon p in pols)
			{
				int nVerts = p.Vertics.Count;
				PositionColored[] points = new PositionColored[nVerts];
				int i = 0;
				foreach (VisualizationPackage.FileLoader.Vertex vert in p.Vertics)
					points[i++] = new PositionColored((float)vert["X"], (float)vert["Y"], (float)vert["Z"], p.DrawingColor);
				VertexBuffer vb = new VertexBuffer(device, nVerts * PositionColored.StrideSize, Usage.None, VertexFormats.Transformed, Pool.Managed, null);
				GraphicsBuffer<PositionColored> current = vb.Lock<PositionColored>(0, nVerts, LockFlags.None);
				current.Write(points);
				Polygons.Add(current);
				vb.Unlock();
			}
		}

		public void SetLines(List<VisualizationPackage.FileLoader.Line> lines)
		{
			if(Polygons != null)
				Polygons.Clear();
			PositionColored V1, V2;
			VertexBuffer vb = new VertexBuffer(device, 2 * lines.Count * PositionColored.StrideSize, Usage.None, VertexFormats.Transformed, Pool.Managed, null);
			Lines = vb.Lock<PositionColored>(0, 2 * lines.Count, LockFlags.None) ;

			foreach (VisualizationPackage.FileLoader.Line ln in lines)
			{
				V1 = new PositionColored((float)ln.Vertext1["X"], (float)ln.Vertext1["Y"], (float)ln.Vertext1["Z"], ln.DrawingColor);
				V2 = new PositionColored((float)ln.Vertex2["X"], (float)ln.Vertex2["Y"], (float)ln.Vertex2["Z"], ln.DrawingColor);
				Lines.Write(new PositionColored[] { V1, V2 });
			}
			vb.Unlock();
		}
	}
}

*/
